import sexprs

#Global counter for labels (created at code generation): 
LABEL_COUNTER = 0

def genMemArrayPrinting():
    code="\n\n /* printing of Hash Memory Array: */\n"
    for i in range(0,50):
        code = code + ("        print_mem(ADDR(IMM("+str(i)+")),"+str(i)+");\n")
    code = code + " /* printing of Hash Memory finished */\n"    
    return code

def type_check(register,scheme_type):
    ans="""
    CMP("""+register+","+scheme_type+""");

    JUMP_NE(TYPE_ERROR);
    """
    return ans

def getNewLabelNumber():
    global LABEL_COUNTER
    LABEL_COUNTER = LABEL_COUNTER + 1
    return LABEL_COUNTER

def itemIndexInList(item,List):
    size = len(List)
    index = 0
    while(index<size):
        if(item == List[index]):
            return index
        index = index + 1
    return -1

def itemIndexInMatrix(item,matrix):
    size = len(matrix)
    major = 0
    while(major<size):
        minor = itemIndexInList(item,matrix[major])
        if(minor!=-1):
            return (major,minor) 
        else:
            major = major + 1
    return (-1,-1)           
     

def define_error(sexpr):
    raise defineError(str(sexpr))

def error(sexpr):
    raise NoMatchingTag("unhandled syntax: "+str(sexpr))

class ReservedWordException(Exception):
    pass

class defineError(Exception):
    pass

class NoMatchingTag(Exception):
    pass

def printList(lst):
    ans =''
    for item in lst:
        ans = ans + ' ' + str(item)
    return ans.strip();

def printTreeList(lst):
    ans =''
    for item in lst:
        ans = ans + ' ' + item.printTree()
    return ans

def PairsToList(sexpr):
    global stateFunction
    lst = []
    while(sexpr.getType()=='Pair'):
        lst.append(stateFunction['init'](sexpr.car))        # add current node car to list, AFTER being tagged
        sexpr = sexpr.cdr
    if(sexpr.getType()!='Nil'):
        lst.append(stateFunction['init'](sexpr))        #case ImproperList, add cdr of last pair to list
    return lst

def tagConstant(sexpr):
    return Constant(sexpr , "")

#precondition - sym.getType()=='Symbol'
def tagSymbol(sym):
    global reservedWords
    try:
        #reservedWords[str(sym)]         - old version - without forcing sym to be Symbol
        reservedWords[sym.symbol]        #we precondition sym to be a Symbol, hence it must have a symbol attribute. if not - we got here because of syntax error, and there is no match 
        raise ReservedWordException(str(sym)) 
    except KeyError:
        return Variable(sym)
    
def quasiQuote_handler(sexpr):
    try:
        if(sexpr.cdr.getType() != 'Nil'):
                raise NoMatchingTag
        return quasiQuote_expander(sexpr.car)    
    except AttributeError:
        raise NoMatchingTag
    
def quasiQuote_expander(sexpr):
    global stateFunction
    if( sexprs.isUnquote(sexpr) ):                           #case sexpr is in "unquote" form
        return stateFunction['init'](sexpr.cdr.car)        #unquote the expression - leave only the "value" of it
    elif( sexprs.isUnquoteSplicing(sexpr) ):                 #case sexpr is in "unquote-splicing" form
        raise NoMatchingTag("unquote-splicing here makes no sense!")
    elif(sexpr.getType()=='Pair'):
        cAr_sexpr = sexpr.car 
        cDr_sexpr = sexpr.cdr
        if( sexprs.isUnquoteSplicing(cAr_sexpr) ):
            appendProc = stateFunction['init'](sexprs.Symbol("append"))
            cAr_reformed_sexpr = stateFunction['init'](cAr_sexpr.cdr.car)
            cDr_reformed_sexpr = quasiQuote_expander(cDr_sexpr) 
            return Applic(appendProc,[cAr_reformed_sexpr,cDr_reformed_sexpr])
            #EITHER:
            #return Constant(sexpr.Pair(cAr_reformed_sexpr,cDr_reformed_sexpr)) 
        elif( sexprs.isUnquoteSplicing(cDr_sexpr) ):
            consProc = stateFunction['init'](sexprs.Symbol("cons"))
            cAr_reformed_sexpr = quasiQuote_expander(cAr_sexpr)
            cDr_reformed_sexpr = stateFunction['init'](cDr_sexpr.cdr.car) 
            return Applic(consProc,[cAr_reformed_sexpr,cDr_reformed_sexpr])
            #EITHER:
            #return Constant(sexpr.Pair(cAr_reformed_sexpr,cDr_reformed_sexpr)) 
        else: 
            consProc = stateFunction['init'](sexprs.Symbol("cons"))
            cAr_reformed_sexpr = quasiQuote_expander(cAr_sexpr)
            cDr_reformed_sexpr = quasiQuote_expander(cDr_sexpr)  
            return Applic(consProc,[cAr_reformed_sexpr,cDr_reformed_sexpr])
            #EITHER:
            #return Constant(sexprs.Pair(cAr_reformed_sexpr,cDr_reformed_sexpr))        
    elif(sexpr.getType()=='Vector'): 
        #I THINK ITS BEST JUST TO TURN VECTOR IN THE READER INTO A PROPER LIST USING CREATELIST, AND THEN SEND IT TO VECTOR - 
        # IT WILL BE EASIER HERE TO TURN IT INTO A LIST THIS WAY, AND I SUSPECT ALSO LATER...
        listToVectorProc = Variable(sexprs.Symbol("list->vector"))
        quasiQuatedList = quasiQuote_expander(sexpr.toList())
        return Applic(listToVectorProc,[quasiQuatedList])
        #EITHER:
        #return Constant(sexprs.Vector(map(quasiQuote_expander,sexpr.vec)) , "'")
    #one way tp continue from here:    
    #elif(sexpr.getType()=='Nil' or sexpr.getType()=='Symbol'): 
    #    return Constant(sexpr,"'")
    #else:
    #    return Constant(stateFunction['init'](sexpr) , "")
    
    #second way tp continue from here - I prefer it because its showing the logic behind better - 
    #a non-pair which is unquoted should be tagged as quoted:
    else:
        return stateFunction['init'](sexprs.Pair(sexprs.Symbol('quote') , sexprs.Pair(sexpr,sexprs.Nil())))
                                     
def cond_handler(sexpr):
    global stateFunction
    try:
        if (sexpr.getType()=='Nil'):
                return Constant(sexprs.Void() , "")
        if (str(sexpr.car.car)=='ELSE'):
                return stateFunction['init'](sexpr.car.cdr.car)
        return IfThenElse(stateFunction['init'](sexpr.car.car),stateFunction['init'](sexpr.car.cdr.car),cond_handler(sexpr.cdr)) #//TODO ask nadav about application and pair
    except AttributeError:        
        raise NoMatchingTag 

def or_handler(sexpr):
    if(sexpr.getType()=='Nil'):
        return Constant(sexprs.Boolean("#f"),"")
    else:
        return Or(PairsToList(sexpr))

def and_handler(sexpr):
    global stateFunction
    try:
        if(sexpr.getType()=='Nil'):
            return Constant(sexprs.Boolean("#t"),"")
        if(sexpr.cdr.getType()=='Nil'):
            return  stateFunction['init'](sexpr.car)
        else:
            return IfThenElse(stateFunction['init'](sexpr.car), and_handler(sexpr.cdr), Constant(sexprs.Boolean("#f") , ""))
    except AttributeError:
        raise NoMatchingTag           
        
def define_handler(sexpr):
    global stateFunction
    global reservedWords
    try:
        if(sexpr.car.getType()=='Pair'):
            name = tagSymbol(sexpr.car.car)
            expr = reservedWords['LAMBDA'](sexprs.Pair(sexpr.car.cdr, sexpr.cdr))
        else:
            if(sexpr.cdr.cdr.getType()=='Nil'):
                name = tagSymbol(sexpr.car)
                expr = stateFunction['init'](sexpr.cdr.car)
            else:
                raise NoMatchingTag    
            
    except AttributeError:
        raise NoMatchingTag
    return Def(name, expr)

def quote_handler(sexpr):
    try:
        if(sexpr.cdr.getType() != 'Nil'):
                raise NoMatchingTag
        return Constant(sexpr.car, "'")    
    except AttributeError:
        raise NoMatchingTag    
       
def let_star_handler(sexpr):

    try:
        if(sexpr.car.getType()=='Nil'):
            return let_handler(sexpr)
            
        if(sexpr.car.cdr.getType()=='Nil'):
            
            return let_handler(sexpr)
        else:
           
            return let_handler(sexprs.Pair(sexprs.Pair(sexpr.car.car,sexprs.Nil()),\
                                                       sexprs.Pair(sexprs.Pair(sexprs.Symbol('LET*'),\
                                                                   sexprs.Pair\
                                                                   (sexpr.car.cdr,sexpr.cdr)),sexprs.Nil())))        
    except AttributeError:
        raise NoMatchingTag
 
def let_handler(sexpr):
    global stateFunction
    try:
        exps = PairsToList(sexpr.cdr)   #sexpr.cdr = let body
        
        sexpr = sexpr.car               #sexpr.car = let parameters couples
        params = []
        paramsValues = []
        while(sexpr.getType()=='Pair'):
            params.append(stateFunction['init'](sexpr.car.car))        # sexpr.car.car = param name
            paramsValues.append(stateFunction['init'](sexpr.car.cdr.car))        # sexpr.car.cdr.car = param value (body)
            sexpr = sexpr.cdr
        if(sexpr.getType()!='Nil'):
            params.append(stateFunction['init'](sexpr.car))        # sexpr.car.car = param name
            paramsValues.append(stateFunction['init'](sexpr.cdr.car))        # sexpr.car.cdr.car = param value (body)
        
        oper = LambdaSimple(params , exps)
        return Applic(oper, paramsValues)    
    except AttributeError:
        raise NoMatchingTag


#Global counter for letrec_handler (YAG function): 
YAG_VAR_COUNTER = 1

def letrec_handler(sexpr):
    global stateFunction
    try:
        global YAG_VAR_COUNTER
        yag = Variable(sexprs.Symbol('YAG'))
        g0 = Variable(sexprs.Symbol('~'+str(YAG_VAR_COUNTER)))
        YAG_VAR_COUNTER = YAG_VAR_COUNTER+1
        yagExps = []
        
        exps = PairsToList(sexpr.cdr)
        
        sexpr = sexpr.car
        params = []
        params.append(g0)
        paramsValues = []
        paramsValues.append(exps)
        while(sexpr.getType()=='Pair'):
            params.append(stateFunction['init'](sexpr.car.car))        # sexpr.car.car = param name
            paramsValues.append( [ stateFunction['init'](sexpr.car.cdr.car) ] )        # sexpr.car.cdr.car = [ param value (body) ]
            sexpr = sexpr.cdr
        if(sexpr.getType()!='Nil'):
            params.append(stateFunction['init'](sexpr.car))        # sexpr.car.car = param name
            paramsValues.append(stateFunction['init'](sexpr.cdr.car))        # sexpr.car.cdr.car = param value (body)
        
        for LEi in paramsValues:
            yagExpI = LambdaSimple(params,LEi)
            yagExps.append(yagExpI)
            
        return Applic(yag,yagExps)
        
    except AttributeError:
        raise NoMatchingTag

def lambda_handler(sexpr):
    try:
        exprsLam = PairsToList(sexpr.cdr)
        
        sexpr = sexpr.car
        if(sexpr.getType()=='Symbol'):
            varparams = tagSymbol(sexpr)
            return LambdaVar(varparams,exprsLam)
        else:
            reqparams = []
            while(sexpr.getType()=='Pair'):
                reqparams.append(tagSymbol(sexpr.car))        # add current node car to list, must be symbol
                sexpr = sexpr.cdr
            if(sexpr.getType()!='Nil'):
                optparams = tagSymbol(sexpr)
                return LambdaOpt(reqparams,optparams,exprsLam)
            else:
                return LambdaSimple(reqparams,exprsLam)       
    except AttributeError:
        raise NoMatchingTag


def if_handler(sexpr):
    global stateFunction
    try:
        cond = stateFunction['init'](sexpr.car)
        sexpr = sexpr.cdr
        consequence = stateFunction['init'](sexpr.car)
        sexpr = sexpr.cdr
        if(sexpr.getType() == 'Nil'):
            alternative = tagConstant(sexprs.Void())
        else:    
            if(sexpr.cdr.getType() != 'Nil'):
                raise NoMatchingTag
            else:
                alternative = stateFunction['init'](sexpr.car)
        return  IfThenElse(cond,consequence,alternative)       
    except AttributeError:
        raise NoMatchingTag         


def applic_handler(sexpr):
    global stateFunction
    oper = stateFunction['init'](sexpr.car)
    operands = PairsToList(sexpr.cdr)
    return Applic(oper,operands)


def init_tag(sexpr):
    global stateFunction   
    if(sexpr.getType()=='Pair'):
        try:
            return reservedWords[str(sexpr.car)](sexpr.cdr)
        except KeyError:
            return applic_handler(sexpr)
    else:
        return stateFunction['init'+sexpr.getType()](sexpr)
    
                 
def true_init_tag(sexpr):
    global stateFunction 
    if(sexpr.getType()=='Pair'):
        try:
            handler = reservedWords[str(sexpr.car)]
            if(handler!=define_error): #define found
                return reservedWords[str(sexpr.car)](sexpr.cdr)
            else:
                return define_handler(sexpr.cdr)
        except KeyError:
            return applic_handler(sexpr)
    else:
        return stateFunction['init'+sexpr.getType()](sexpr)             

    
class AbstractSchemeExpr:
    @staticmethod
    def parse(inputStr):
        (ast,r) = sexprs.AbstractSexpr.readFromString(inputStr)
        m = stateFunction['trueinit'](ast)
        return (m,r)
    
    def semantic_analysis(self):
        return self.debruijn().annotateTC()
    
    def __str__(self):
        pass
    
    def printTree(self):
        pass
    
    def debruijn(self):
        return self.av([[]],[])
        
    def av(self,env,parList):    
        return self
    
    def annotateTC(self):
        return self.atp(False)
    
    def atp(self,isTailPosition):
        return self
    
    def gatherConstants(self,constantGatherer):
        return constantGatherer
    
    def gatherGlobalVars(self,globalVarTable,nextFreeAddress):
        return nextFreeAddress
    
    def code_gen(self,constantTable,constantLookup,envSize,globalVarTable):
        return ""
    
    
    
class Constant(AbstractSchemeExpr):
    def __init__(self,c,q):
        self.c = c
        self.isQuoted = q
    def __str__(self):
            return str(self.isQuoted)+str(self.c)
    def printTree(self):
        return 'Constant('+self.c.printTree()+')'
    def isQuoted(self):
        return self.isQuoted!=""
    def gatherConstants(self,constantGatherer):
        conststr = str(self.c)
        constantGatherer[conststr] = self.c
        return constantGatherer
    def code_gen(self,constantTable,constantLookup,envSize,globalVarTable):
        code =" /* Constant : " + str(self) +" */  \n"
        constStringRep = str(self.c)        #have to be str(self.c) , otherwise can add a quate mark - ' - to the string representation!
        if constStringRep=='':              #case constant is Void
            constStringRep = '~void'         #change string Representation to '~void'
        constPointer = constantTable[constantLookup[constStringRep]][0]
        code = code + "        MOV(R0,IMM(" + str(constPointer) +"));\n"
        code = code + " /* Constant finished */  \n"
        return code
        
        
class Variable(AbstractSchemeExpr):
    def __init__(self,var):
        self.var = var
    def __str__(self):
        return str(self.var)
    def printTree(self):
        return 'Variable('+self.var.printTree()+')'
    def av(self,env,parList):
        minor = itemIndexInList(self,parList)    
        if(minor!=-1):
            return VarParam(self.var,minor)
        else:
            (major,minor) = itemIndexInMatrix(self,env)
            if(major!=-1 and minor!=-1):
                return VarBound(self.var,major,minor)
            else:
                return VarFree(self.var)
    def __eq__(self,y):
        return self.var==y.var 
    
               

class VarFree(Variable):
    def __init__(self,var):
        Variable.__init__(self,var) 
    def printTree(self):
        return 'VarFree('+self.var.printTree()+')'
    def addVarFreeAddress(self,address):
        self.varAddress = address
    def code_gen(self,constantTable,constantLookup,envSize,globalVarTable):
        code = " /* VarFree : " + str(self) +" */    \n"
        varAddress = globalVarTable[str(self.var)][0]
        code = code + "        MOV(R0,IND(" + str(varAddress) + "));    \t\t\t// R0 = variable " + str(self.var) + " which point to addr " + str(varAddress) + "\n"
        code = code + " /* VarFree finished */  \n"
        return code        
        
class VarParam(Variable):
    def __init__(self,var,minor):
        Variable.__init__(self,var)
        self.minor = minor
    def printTree(self):
        return 'VarParam('+self.var.printTree()+', '+str(self.minor)+')'
    def code_gen(self,constantTable,constantLookup,envSize,globalVarTable):
        code = " /* VarParam : " + str(self) +" */ \n"
        code = code + "        MOV(R0,FPARG(" + str(self.minor + 2) + ")); \n"         # R0 = argument[self.minor]  ( +2 because of env and n) 
        code = code + " /* VarParam finished */ \n"
        return code    
    

class VarBound(Variable):        
    def __init__(self,var,major,minor):
        Variable.__init__(self,var)
        self.major = major
        self.minor = minor
    def printTree(self):
        return 'VarBound('+self.var.printTree()+', '+str(self.major)+', '+str(self.minor)+')'
    def code_gen(self,constantTable,constantLookup,envSize,globalVarTable):
        code = " /* VarBound : " + str(self) +" */ \n"
        code = code + """        MOV(R0,FPARG(0));            \t\t// R0 = Env
        MOV(R0,INDD(R0,""" + str(self.major) + """));            \t\t// R0 = Env[major]
        MOV(R0,INDD(R0,""" + str(self.minor+1) + """));            \t\t// R0 = Env[major][minor+1]    - minor+1 BECAUSE OF T_ENVVEC!
        """
        code = code + " /* VarBound finished */ \n"
        return code        
 
    
class IfThenElse(AbstractSchemeExpr):
    def __init__(self,cond,t,f):
        self.cond = cond
        self.t = t
        self.f = f
    def __str__(self):
        return '(IF '+str(self.cond)+' '+str(self.t)+' '+str(self.f)+')'
    def printTree(self):
        return 'IfThenElse('+self.cond.printTree()+','+self.t.printTree()+','+self.f.printTree()+')'
    def av(self,env,parList):
        self.cond = self.cond.av(env,parList)
        self.t = self.t.av(env,parList)
        self.f = self.f.av(env,parList)
        return self
    def atp(self,isTailPosition):
        self.cond = self.cond.atp(False)
        self.t = self.t.atp(isTailPosition)
        self.f = self.f.atp(isTailPosition)
        return self
    def gatherConstants(self,constantGatherer):
        constantGatherer = self.cond.gatherConstants(constantGatherer)
        constantGatherer = self.t.gatherConstants(constantGatherer)
        constantGatherer = self.f.gatherConstants(constantGatherer)
        return constantGatherer
    def code_gen(self,constant_table,lookup_table,envSize,globalVarTable):
        ans=" /* If :  " + str(self) +" */  \n//code for cond\n"
        ans=ans+self.cond.code_gen(constant_table,lookup_table,envSize,globalVarTable)
        #false = constant_table[lookup_table["#f"]][0]
        ans=ans+"//check if false\n\t\tCMP(R0, IMM(3));\n"
        labelNum = getNewLabelNumber()  
        ans=ans+"\t\tJUMP_EQ(IF_ALT_"+str(labelNum)+");\n" 
        ans=ans+"//code for consequence\n"+self.t.code_gen(constant_table,lookup_table,envSize,globalVarTable)
        ans=ans+"\t\tJUMP(END_IF_"+str(labelNum)+""");
IF_ALT_"""+str(labelNum)+":\n" 
        ans=ans+"//code for alternative\n"+self.f.code_gen(constant_table,lookup_table,envSize,globalVarTable)+"END_IF_"+str(labelNum)+":"
        ans = ans + " /* If finished */  \n"
        return ans 

      
class AbstractLambda(AbstractSchemeExpr):
    def __init__(self,exprs):
        self.exprs = exprs
    def av(self,env,parList):
        newExprs = []
        newEnv = [parList]+env
        for expr in self.exprs:
            newExpr = self.avOnExpr(expr,newEnv) 
            newExprs.append(newExpr)
        self.exprs = newExprs 
        return self
    def avOnExpr(self,expr,newEnv):
        pass
    def atp(self,isTailPosition):
        newLastExpr =  self.exprs.pop().atp(True)
        newExprs = []
        for expr in self.exprs:
            newExpr = expr.atp(False) 
            newExprs.append(newExpr)
        newExprs.append(newLastExpr)
        self.exprs = newExprs
        return self
    def gatherConstants(self,constantGatherer):
        for expr in self.exprs:
            constantGatherer = expr.gatherConstants(constantGatherer)
        return constantGatherer
    def code_gen(self,constantTable,constantLookup,envSize,globalVarTable):
        code = " /* Lambda : " + str(self) +" */  \n"
        code = code + "\t\t//registers saving \n \t\tPUSH(R1); \n \t\tPUSH(R2); \n \t\tPUSH(R3); \n \t\tPUSH(R4); \n"    #registers saving
        #R1 = new env size (growing by 1):
            #####################################################
            # NEED TO CHECK - DO I DO THE BELOW CODE IN ASM, C, OR CAN I ALREADY KNOW IN PYTHON THE env SIZE
            # IF IN ASM, THEN HOW DO I FIND OUT THE ENV SIZE?
            #####################################################
        code = code + "\t\t//allocating space for new env: \n        PUSH(IMM(" + str(envSize+1) + "));"  #allocating space for new env (size growing by 1)    
        code = code + """    
        CALL(MALLOC);
        DROP(1);
        MOV(R1,R0);        //R1 = new env address    \n"""
        #R2 = old env address from stack:
        
        code = code + "        MOV(R2,FPARG(0));        \t\t//R2 = old env address from stack \n"
        code = code + "        //copying of old env to new:\n"
        for i in range(envSize):                                        #copying of old env to new
            code = code + "        MOV(INDD(R1,"+ str(i+1) +"),INDD(R2," + str(i) +"));\n"
        code = code + "        //finish copying \n"
        #R4 = n - current number of params:
        code = code + "        MOV(R4,FPARG(1));        \t\t//R4 = n - current number of params \n"
        code = code + "        INCR(R4);                \t// R4 = n + 1 - for T_ENVVEC recognizer \n"
        code = code + "        PUSH(R4);  \n"    
        #allocating space for new vector in env:
        code = code + """    
        CALL(MALLOC);                       //allocating space for new env variables vector 
        DROP(1); \n
        MOV(INDD(R1,0),R0);                //R1[0] = newEnv[0] =pointer to new env vector \n"""
        code = code + "        //copying of params to new env variables vector: \n"
        #copying of params to new env arguments vector:
            #####################################################
            # NEED TO CHECK - DO I DO THE BELOW CODE IN ASM, C, OR CAN I ALREADY KNOW IN PYTHON THE n SIZE
            # IF IN PYTHON, THEN I NEED TO ADD IT TO THE CODE_GEN SIGNATURE, AND MAKE SURE SOME METHOD CHANGES IT!
            # I THINK IT SOULD BE THIS METHOD THAT CHANGES IT, JUST LIKE THE ENV SIZE - SINCE ITS THE ONLY ONE KNOWING THE 
            # TRUE NUMBER OF PARAMETERS. AND IF SO, I SHOULD MAKE A METHOD AT EACH TYPE OF LAMBDA RETURNING THE NUMBER OF PARAMETERS
            # AND IT WILL LOOK LIKE THIS:
            # for i in range(n):                                        # copying arguments of stack to new vector
            #     code = code + "        MOV(INDD(R0,"+ str(i) +"),FPARG(" + str(i+2) +"));\n"
            # WILL NEED TO UNMARK FUNCTION CALL TO getNumOfParameters DOWN, AND ADD PARAMETER n TO EACH code_gen 
            #####################################################
        bodyLabelNum = getNewLabelNumber()
        code = code + """
        MOV(INDD(R0,0),T_ENVVEC);            \t\t// R0[0] = T_ENVVEC recognizer - recognize a env vector start


	PUSH(R7);
	MOV(R7,1);
	ENVVEC_LOOP"""+str(bodyLabelNum)+""":
	
//        for(i=1,j=2;i<R4;i++,j++)        {
//	     MOV(INDD(R0,i),FPARG(j));

            MOV(INDD(R0,R7),FPARG(R7+1));
            INCR(R7);
	    CMP(R7,R4);	
	    JUMP_LT(ENVVEC_LOOP"""+str(bodyLabelNum)+""");
	POP(R7);
//        }"""
        code = code +  """        //finish copying """
        #creating the closure scheme object:
        
        code = code + """        //creating new T_CLOSURE scheme object:
        PUSH(IMM(3));
        CALL(MALLOC);                       //allocating space for new T_CLOSURE scheme object (=R0) 
        DROP(1);
        MOV(INDD(R0,0),T_CLOSURE);          //R0[0]=T_CLOSURE
        MOV(INDD(R0,1),R1);                 //R0[1]=new env
        MOV(INDD(R0,2),LABEL(LAMBDA_BODY_""" + str(bodyLabelNum) +"));            //R0[2]=lambda body label pointer \n"     #NEED TO CHECK IF ITS ADDR(B) OR LABEL(B)!!!
        code = code + "        JUMP(LAMBDA_BUILD_FINISH_" + str(bodyLabelNum) + "); \n\n"
        code = code + "LAMBDA_BODY_" + str(bodyLabelNum) +""":
        PUSH(FP);
        MOV(FP,SP);
        \n"""
        
        #performing stack arguments check ,and fixing incase its needed (in LambdaVar and LambdaOpt only!)
        code = code + self.checkArguments()
        #increasing envSize since we just expanded the env with a new one:
        envSize = envSize + 1
        
        #changing number of arguments of stack, since this lambda was just executed:
        #n = self.getNumOfParameters()                                         # n = new number of arguments on stack
        
        #performing codegen on each expression in the lambda body:           
        for expr in self.exprs:
            code = code + expr.code_gen(constantTable,constantLookup,envSize,globalVarTable)
        
        code = code + """
        POP(FP);
        RETURN;         \n
        LAMBDA_BUILD_FINISH_""" + str(bodyLabelNum) + ":      \n"
        code = code + "\t\t//registers reviving \n \t\tPOP(R4); \n \t\tPOP(R3); \n \t\tPOP(R2); \n \t\tPOP(R1); \n "    #registers reviving
        code = code + " /* Lambda finished */  \n"
        return code
    
    ############################## OLD VERSION!! - without TPApplic ##########
    # ------------------------------
    #re-arrange optional arguments in stack
    # ------------------------------
    def arrangeOptionalArguments(self,reqParamsAmount):
        code = ""
        opLabelNum = getNewLabelNumber()
        code = code + "\t\t//registers saving \n \t\tPUSH(R1); \n \t\tPUSH(R2); \n \t\tPUSH(R3); \n"    #registers saving
        code = code + """        MOV(R1,IMM(2));    \t\t\t//R1 = SOB_NIL  (sitting by defualt at address 2)
        MOV(R2,FPARG(1));    \t\t\t// R2 = n    (number of arguments always at fp+1)

/*##########code for replacing C for loop*/

	PUSH(R5);
	MOV(R5,R2);
	INCR(R5);
        OP_ARGUMENTS_LOOP"""+str(opLabelNum)+""":

/*##########################################*/
//        for(i=R2+1;i>=""" + str(reqParamsAmount+2) + """;i--){        \t\t//i=reqParamsAmount+2 so point to first Optional argument from FP  (arrange as 0:env , 1:n , 2:Arg_1 , ... , n+2:Arg_n)

//            MOV(R3,FPARG(i));
            MOV(R3,FPARG(R5));
            PUSH(R1);        \t\t//2 param = R1 = existing_aggrated_list 
            PUSH(R3);        \t\t//1 param = R3 = argument at i
            CALL(MAKE_SOB_PAIR);        \t\t//  Cons(arg_i , existing_list)
            DROP(2);
            MOV(R1,R0);        \t\t//  R1 = aggregated list of arguments!
/*##########code for replacing C for loop*/
        

	    DECR(R5);
	    CMP(R5,"""+ str(reqParamsAmount+2) + """);
	    JUMP_GE(OP_ARGUMENTS_LOOP"""+str(opLabelNum)+""");
	    POP(R5);

/*##########################################*/
//	    }

        // fixing the stack:
        MOV(R2,""" + str(reqParamsAmount) + """);    \t\t\t// R2 = req number of params
        INCR(R2);    \t\t\t// R2 = fixed number of arguments = reqparams + 1 opt param
        MOV(R3,FP);                \t//R3 = frame pointer
        SUB(R3,4);                    // R3 = FP - 4 = n  =>  FPARG(0) SITS IN (FP - 3) !!!!


//#####################################commented out for tpapplic
        MOV(STACK(R3),R2);            // n = new fixed number of arguments
//#########################################################################
        SUB(R3,R2);                  // R3 = FP -4 - R2 = optional argument index in stack
        MOV(STACK(R3),R1);       \t// R3 => FPARG(2+R2) => optional argument = R3 index on the stack to be the new opt param R1 - a aggregeted list!
        """
        
        code = code + "\t\t//registers reviving \n\t\tPOP(R3); \n\t\tPOP(R2); \n\t\tPOP(R1); \n"    #registers reviving
        return code
    ####################################################
    
    # ------------------------------
    #re-arrange optional arguments in stack
    # ------------------------------
    '''def arrangeOptionalArguments(self,reqParamsAmount):
        code = ""
        opLabelNum = getNewLabelNumber()
        code = code + "\t\t//registers saving \n \t\tPUSH(R1); \n \t\tPUSH(R2); \n \t\tPUSH(R3); \n \t\tPUSH(R4); \n"    #registers saving
        code = code + """        MOV(R1,IMM(2));    \t\t\t//R1 = SOB_NIL  (sitting by defualt at address 2)
        MOV(R2,FPARG(1));    \t\t\t// R2 = n    (number of arguments always at fp+1)
//##########code for replacing C for loop
	PUSH(R5);
	MOV(R5,R2);
	INCR(R5);
        OP_ARGUMENTS_LOOP"""+str(opLabelNum)+""":
//#########################
//for(i=R2+1;i>=""" + str(reqParamsAmount+2) + """;i--){        \t\t//i=reqParamsAmount+2 so point to first Optional argument from FP  (arrange as 0:env , 1:n , 2:Arg_1 , ... , n+2:Arg_n)
            MOV(R3,FPARG(i));
            PUSH(R1);        \t\t\t//2 param = R1 = existing_aggrated_list 
            PUSH(R3);        \t\t\t//1 param = R3 = argument at i
            CALL(MAKE_SOB_PAIR);        \t\t\t//  Cons(arg_i , existing_list)
            DROP(2);
            MOV(R1,R0);        \t\t\t//  R1 = aggregated list of arguments!
//##########code for replacing C for loop
	    CMP(R5,"""+ str(reqParamsAmount+2) + """);
            DECR(R5);
	    JUMP_GE(OP_ARGUMENTS_LOOP"""+str(opLabelNum)+""");
	    POP(R5);
//        }
//#########################
        // fixing the stack:
        MOV(R2,""" + str(reqParamsAmount) + """);    \t\t\t// R2 = req number of params
        MOV(R3,FP);                \t\t\t\t//R3 = frame pointer
        SUB(R3,4);                   \t\t\t\t // R3 = FP - 4 = n  =>  FPARG(0) SITS IN (FP - 3) !!!!
        MOV(R4,R3);                   \t\t\t\t // R4 = R3 = n index in stack
        SUB(R3,FPARG(1));
        DECR(R3);            \t\t\t\t// R3 = FP - 4 - n - 1 = magic number place on frame
        MOV(STACK(R3),R1);       \t\t\t\t// R3 => R3 index on the stack (magic number place!) to be the new opt param R1 - a aggregeted list!
        INCR(R3);            \t\t\t\t// R3 = magic number index + 1 = index of last argument given by frame
        MOV(FPARG(1),""" + str(reqParamsAmount + 1) + """);            \t\t// n = new fixed number of arguments = req Params Amount + 1    
        SUB(R4,R2);          \t\t\t\t // R4 = FP -4 - R2 = last Req argument index in stack \n"""
        
        for i in range(0,reqParamsAmount):
            code = code + """        MOV(STACK(R3),STACK(R4));    \t\t// mov argument"""+ str(reqParamsAmount-i) +""" 
        INCR(R3);
        INCR(R4); \n"""
        
        code = code + """        MOV(STACK(R3),STACK(R4));        \t\t// mov parameter N (pay attention its already been altered!)  
        INCR(R3);
        INCR(R4); \n"""
        code = code + """        MOV(STACK(R3),STACK(R4));        \t\t// mov parameter env
        INCR(R3);
        INCR(R4); \n"""
        code = code + """        MOV(STACK(R3),STACK(R4));        \t\t// mov parameter Return address
        INCR(R3);
        INCR(R4); \n"""
        code = code + """        MOV(STACK(R3),STACK(R4));        \t\t// mov parameter oldFP
        MOV(R2,R3);                \t\t// R2 = olfFP index in stack
        INCR(R2);                  \t\t// R2 = olfFP index + 1 = last frame index + 1 = FP supposed index in stack
        MOV(FP,R2);                \t\t// fixing framepointer    \n"""
        
        for i in range(1,5):
            code = code + """        INCR(R3);
        INCR(R4);
        MOV(STACK(R3),STACK(R4));       \t\t\t// mov register R""" + str(i) + """ that was saved \n"""
        
        code = code + """        INCR(R3);
        MOV(SP,R3);                \t\t// dropping the stack      \n"""                         
        code = code + "\t\t//registers reviving \n\t\tPOP(R4); \n\t\tPOP(R3); \n\t\tPOP(R2); \n\t\tPOP(R1); \n"    #registers reviving
        return code'''
    
    # ------------------------------
    #check if there is at least reqparams amount of params in stack
    # ------------------------------
    def checkReqArgumentsAmount(self,reqParamsAmount):
        code = """\t\t/* arguments amount check */
        PUSH(R1);        \t\t//register saving
        MOV(R1,FPARG(1));                    // R1 = n
        CMP(R1,"""+ str(reqParamsAmount) +""");        \t\t\t// if n == number of required params
        JUMP_NE(TYPE_ERROR);
        POP(R1);            \t\t//register reviving
        /* arguments amount check finished */ \n"""
        return code
    
    # ------------------------------
    #checking of the arguments given the lambda at running of the lambda body
    # ------------------------------
    def checkArguments(self):
        pass
    
    def getNumOfParameters(self):
        pass

class LambdaSimple(AbstractLambda):
    def __init__(self,params,exprs):
        AbstractLambda.__init__(self,exprs)
        self.params = params
    def __str__(self):
        return '(LAMBDA ('+printList(self.params)+') '+printList(self.exprs)+')'
    def printTree(self):
        return 'LambdaSimple(['+printTreeList(self.params)+' ],['+printTreeList(self.exprs)+' ])'
    def avOnExpr(self,expr,newEnv):
        return expr.av(newEnv , self.params)
    
    ##################### old behavior - without TPApplic!!!! #######################
    '''def checkArguments(self):
        #return self.checkReqArgumentsAmount(len(self.params))
        return ""'''
    #################################################################################
    
    def checkArguments(self):
        #return self.checkReqArgumentsAmount(len(self.params))
        code = ""
        return code
        
    def getNumOfParameters(self):
        return len(self.params)
    
    
class LambdaOpt(AbstractLambda):
    def __init__(self,reqparams,optparams,exprs):
        AbstractLambda.__init__(self,exprs)
        self.reqparams = reqparams
        self.optparams = optparams
    def __str__(self):
        return '(LAMBDA ('+printList(self.reqparams)+' . '+str(self.optparams)+') '+printList(self.exprs)+')'
    def printTree(self):
        return 'LambdaOpt(['+printTreeList(self.reqparams)+' ],'+self.optparams.printTree()+',['+printTreeList(self.exprs)+' ])'
    def avOnExpr(self,expr,newEnv):
        return expr.av(newEnv , self.reqparams+[self.optparams])
    def checkArguments(self):
        code = "\t\t/*  re-arrenging Opt arguments: */\n"
        reqParamsAmount = len(self.reqparams)
        #code = code + self.checkReqArgumentsAmount(reqParamsAmount)                 #check if there is at least reqparams amount of params in stack
        code = code + self.arrangeOptionalArguments(reqParamsAmount)                #re-arrange optional arguments in stack
        code = code + "\t\t/* Opt re-arrenging finished */\n"
        return code
    def getNumOfParameters(self):
        return len(self.reqparams)+1
    
     
class LambdaVar(AbstractLambda):
    def __init__(self,params,exprs):
        AbstractLambda.__init__(self,exprs)
        self.varparam = params
    def __str__(self):
        return '(LAMBDA '+str(self.varparam)+' '+printList(self.exprs)+')'
    def printTree(self):
        return 'LambdaVar('+self.varparam.printTree()+',['+printTreeList(self.exprs)+' ])'    
    def avOnExpr(self,expr,newEnv):
        return expr.av(newEnv , [self.varparam])
    def checkArguments(self):
        code = "\t\t/*  re-arrenging Variadic arguments: */\n"
        reqParamsAmount = 0
        code = code + self.arrangeOptionalArguments(reqParamsAmount)                #re-arrange optional arguments in stack
        code = code + "\t\t/* Variadic re-arrenging finished */\n"
        return code
    def getNumOfParameters(self):
        return 1
    
    
# Applic
#parameters:
#oper - schemeExpr
#operands - list of SchemeExpr / empty list
class Applic(AbstractSchemeExpr):
    def __init__(self,operator,operands):
        self.operator = operator
        self.operands = operands
    def __str__(self):
        return '('+str(self.operator)+' '+printList(self.operands)+')'
    def printTree(self):
        return 'Applic('+self.operator.printTree()+',['+printTreeList(self.operands)+' ])'
    def av(self,env,parList):
        newOperands = []
        for operand in self.operands:
            newOperand = operand.av(env,parList) 
            newOperands.append(newOperand)
        self.operands = newOperands
        self.operator = self.operator.av(env,parList) 
        return self
    def atp(self,isTailPosition):
        newOperands = []
        for operand in self.operands:
            newOperand = operand.atp(False) 
            newOperands.append(newOperand)
        self.operands = newOperands
        self.operator = self.operator.atp(False)
        if(isTailPosition==True):                           #if you are a application at a tail position
            return ApplicTP(self.operator,self.operands)    #change to ApplicTP
        else:
            return self                                     #dont change, you are not an application at tail position
    def gatherConstants(self,constantGatherer):
        constantGatherer = self.operator.gatherConstants(constantGatherer)
        for operand in self.operands:
            constantGatherer = operand.gatherConstants(constantGatherer)
        return constantGatherer
       
    ##################### old behavior - without TPApplic!!!! #######################   
    def code_gen(self,constant_table,lookup_table,envSize,globalVarTable):
        ans = "/*////Application of " + str(self.operator) +" on " + str(printList(self.operands))+"*/\n"
        magicNumber = "T_FRAME_END"                         # to change if T_FRAME_END is no longer a type!
        ans = ans+"        PUSH(IMM("+ magicNumber +"));        //push magic number\n" 
        for op in reversed(self.operands):
            ans=ans+op.code_gen(constant_table,lookup_table,envSize,globalVarTable)+"\n\t\tPUSH(R0);\n"
        ans=ans+str(self.operator.code_gen(constant_table,lookup_table,envSize,globalVarTable))+"\n"
        ans=ans+type_check("IND(R0)","T_CLOSURE")+"""
        PUSH("""+str(len(self.operands))+""");        //push N\n
        PUSH(INDD(R0,1));     //push env
        CALLA(INDD(R0,2));    //execute lambda body
        DROP(2); //drop env and N
        DROP("""+str(len(self.operands)+1)+"""); //drop params + magic number
        /* application finished */ \n"""
        return ans 
    ###################################################################################
    
    '''def code_gen(self,constant_table,lookup_table,envSize,globalVarTable):
        ans = "/////Application of " + str(self.operator) +" on " + str(printList(self.operands))+"\n"
        magicNumber = "T_FRAME_END"                         # to change if T_FRAME_END is no longer a type!
        ans = ans+"        PUSH("+ magicNumber +");        //push magic number\n" 
        for op in reversed(self.operands):
            ans=ans+op.code_gen(constant_table,lookup_table,envSize,globalVarTable)+"\n\t\tPUSH(R0);\n"
        ans=ans+str(self.operator.code_gen(constant_table,lookup_table,envSize,globalVarTable))+"\n"
        ans=ans+type_check("IND(R0)","T_CLOSURE")+"""
        PUSH("""+str(len(self.operands))+""");        \t\t\t//push N\n
        PUSH(INDD(R0,1));     \t\t\t//push env
        CALLA(INDD(R0,2));    \t\t\t//execute lambda body
        DROP(1);        \t\t\t//drop env
        POP(R8);        \t\t\t// R8 = n
        
        ////////////////
        INCR(R8);          \t\t\t\t // only because of magic number - need to make it go away after i fix lambda simple to change n to n+1!
        /////////////////
        
        /* ///////////////// ######################### trying change //////////////////////////
        DECR(R8);         \t\t\t\t// in new change, the magic number will be dropped anyway in lambda body, so after return there is no magic number on stack
        ///////////////// ######################### ////////////////////////// */
        
        DROP(R8);         \t\t\t\t//drop params + magic number
        /* application finished */ \n"""
        return ans''' 
       
class ApplicTP(Applic):
    def __init__(self,operator,operands):
        Applic.__init__(self,operator,operands)
    def printTree(self):
        return 'ApplicTP('+self.operator.printTree()+',['+printTreeList(self.operands)+' ])'    
    '''def code_gen(self,constant_table,lookup_table,envSize,globalVarTable):
        ans = " /* Tail Applic of " + str(self.operator) +" , on: " + str(printList(self.operands))+ " */ \n"
        magicNumber = "T_FRAME_END"                         # to change if T_FRAME_END is no longer a type!
        ans = ans+"        PUSH("+ magicNumber +");        //push magic number\n" 
        for op in reversed(self.operands):
            ans=ans+op.code_gen(constant_table,lookup_table,envSize,globalVarTable)+"\n\t\tPUSH(R0);\n"
        ans=ans+str(self.operator.code_gen(constant_table,lookup_table,envSize,globalVarTable))+"\n"
        ans=ans+type_check("IND(R0)","T_CLOSURE")+"""
        PUSH("""+str(len(self.operands))+""");        //push N\n
        PUSH(INDD(R0,1));     //push env \n"""
        ##### Here starts the tail call unique part:
        ans = ans +"""        /* Here starts the tail call unique part: */
        MOV(R9,FPARG(-1));        \t\t// R9 = previous frame return address
        MOV(R10,FPARG(-2));       \t\t// R10 = previous frame oldFP
        
        //copying new frame information over old frame information
        MOV(R13,FP);
        SUB(R13,4);                \t\t// R13 = FP - 4 = previous frame last argument
        SUB(R13,FPARG(1));         \t\t// R13 = FP - 4 - prevoius frame n (number of previous frame arguments) = last previous frame argument addr on stack
        MOV(R12,FP);        \t\t\t// R12 = FP = pointer to first thing being pushed in the new frame - its the magic number!
        
        /* ///////////////// ######################### trying change //////////////////////////
        MOV(STACK(R13),STACK(R12));            \t\t// copying magic number - lambda_body is always assuming the frame has a maigc number!
        INCR(R13);
        ///////////////// ######################### ////////////////////////// */
        
        INCR(R12);            \t\t// because of magic number!
        \n"""
        for i in range(0,len(self.operands)):
            ans = ans + """        MOV(STACK(R13),STACK(R12));            \t\t// copying New Frame argument """ + str(i) + """ over the old frame space on the stack
        INCR(R12);        \t\t// R12 = next NEW frame argument - from bottom to top
        INCR(R13); \n"""
        ans = ans + """        MOV(STACK(R13),STACK(R12));            \t\t// copying New Frame N ,over the old frame space on the stack
        INCR(R12);
        INCR(R13);
        MOV(STACK(R13),STACK(R12));            \t\t// copying New Frame ,env over the old frame space on the stack
        INCR(R13);        
        MOV(SP,R13);            \t\t// updating the stack pointer - just like dropping the unnesesery info on the frame
        
        PUSH(R9);        \t\t\t// push previous frame Return address again
        MOV(FP,R10);        \t\t// FP = R10 = previous frame oldFP - the PUSH(FP) at the start of LAMBDA_BODY will take care to push it to the stack, as the oldFrame
        JUMPA(INDD(R0,2));    //execute lambda body - but without pushing a new return address!! \n"""
        return ans + " /* TP Applic finished */ \n"'''    
         
class Or(AbstractSchemeExpr):
    def __init__(self,operands):
        self.operands = operands
    def __str__(self):
        return '(OR '+printList(self.operands)+')'
    def printTree(self):
        return 'Or(['+printTreeList(self.operands)+'])'
    def av(self,env,parList):
        newOperands = []
        for operand in self.operands:
            newOperand = operand.av(env,parList) 
            newOperands.append(newOperand)
        self.operands = newOperands 
        return self
    def atp(self,isTailPosition):
        newLastOperand =  self.operands.pop().atp(isTailPosition)
        newOperands = []
        for operand in self.operands:
            newOperand = operand.atp(False) 
            newOperands.append(newOperand)
        newOperands.append(newLastOperand)
        self.operands = newOperands
        return self
    def gatherConstants(self,constantGatherer):
        for operand in self.operands:
            constantGatherer = operand.gatherConstants(constantGatherer)
        return constantGatherer
    def code_gen(self,constantTable,constantLookup,envSize,globalVarTable):
        code = " /* Or " + str(self) +" */    \n"
        code = code + "\t\tPUSH(R1);        //registers saving \n"    #registers saving
        false = constantTable[constantLookup["#f"]][0]
        code = code + "        MOV(R1,IMM(" + str(false) +"));        //R1 = false SOB \n"  #saving false SOB in R1
        labelNum = getNewLabelNumber()
        for operand in self.operands:
            code = code + "\t\t//eval operand: \n"
            code = code + operand.code_gen(constantTable,constantLookup,envSize,globalVarTable)
            # check whether its false, if so then continue evaluate next operand
            code = code + "\t\t//check whether its false: \n"
            code = code +"        CMP(R0,R1); \n"
            code = code + "        JUMP_NE(OR_FINISH_"+str(labelNum)+"); \n"
        code = code + "        OR_FINISH_"+str(labelNum)+": \n"    
        code = code + "\t\tPOP(R1);        //registers reviving\n"    #registers reviving
        code = code + " /* Or finished */  \n"
        return code    
        

class Def(AbstractSchemeExpr):
    def __init__(self,var,exp):
        self.var = var
        self.exp = exp
    def __str__(self):
        return '(DEFINE '+str(self.var)+' '+str(self.exp)+')'
    def printTree(self):
        return 'Def('+self.var.printTree()+','+self.exp.printTree()+')'
    def av(self,env,parList):
        self.var = self.var.av([[]],[])       #self.var is a Variable for sure - already checked during "parse" method! hence this line will always produce a VarFree 
        self.exp = self.exp.av(env,parList)
        return self
    def atp(self,isTailPosition):
        self.exp = self.exp.atp(False)
        return self
    def gatherConstants(self,constantGatherer):
        return self.exp.gatherConstants(constantGatherer)
    def gatherGlobalVars(self,globalVarTable,nextFreeAddress):
        varStringRep = str(self.var)
        #void = constantTable[constantLookup['~void']][0]        # each globalVar will get at first a pointer to T_VOID as value
        if not globalVarTable.get(varStringRep):
            globalVarTable[varStringRep] = [nextFreeAddress , 1]            # 1 - default pointer - pointer will be changed in runtime!
            nextFreeAddress = nextFreeAddress + 1
        return nextFreeAddress
    
    def code_gen(self,constantTable,constantLookup,envSize,globalVarTable):
        code = "/* Define: " + str(self) +" */    \n"
        code = code + "        // evaluate the exprssion:\n\n"
        code = code + self.exp.code_gen(constantTable,constantLookup,envSize,globalVarTable)
        code = code + "\n        // change the global variable to point to the evaluated exprssion's result:\n"
        varAddress = globalVarTable[str(self.var)][0]
        code = code + "        MOV(IND(" + str(varAddress) + "),R0);    \t\t\t// variable " + str(self.var) + " at address: " + str(varAddress) + "\n"
        void = constantTable[constantLookup['~void']][0]        # define return void after creation
        code = code + "        MOV(R0,IMM(" + str(void) +"));        \t\t// R0 = VOID = Define return value \n"
        code = code + "/* Define finished */  \n"
        return code 
         
    
reservedWords = {
    'QUOTE': quote_handler,
    'UNQUOTE': error,
    'UNQUOTE-SPLICING': error,
    'QUASIQUOTE': quasiQuote_handler,
    'LAMBDA': lambda_handler,
    'IF': if_handler,
    'DEFINE': define_error,
    'OR': or_handler,
    'AND': and_handler,
    'LET': let_handler,
    'LETREC': letrec_handler,
    'LET*': let_star_handler,
    'COND': cond_handler,
}


stateFunction = {
    'initNumber':tagConstant,
    'initNil':tagConstant,
    'initChar':tagConstant,
    'initString':tagConstant,
    'initBoolean':tagConstant,
    'initVector':tagConstant, 
    'initSymbol':tagSymbol,
    'init':true_init_tag,
    'trueinit':true_init_tag             
                 }
